package com.xxx.mch.service.impl;

import com.xxx.mch.config.MchInterepter;
import com.xxx.mch.dao.*;
import com.xxx.mch.domain.dto.DepartmentScoreSummary;
import com.xxx.mch.domain.dto.KaopinDto;
import com.xxx.mch.domain.dto.UserReduceDto;
import com.xxx.mch.domain.po.*;
import com.xxx.mch.domain.request.KaopinRequest;
import com.xxx.mch.exception.BusinessException;
import com.xxx.mch.feign.HiveFeignClient;
import com.xxx.mch.service.SystemConfigService;
import java.math.BigDecimal;
import java.math.RoundingMode;
import javax.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.weekend.Weekend;
import tk.mybatis.mapper.weekend.WeekendCriteria;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author FengHaixin
 * @description 一句话描述该文件的用途
 * @date 2020-02-25
 */
@Service
public class DepartmentServiceImpl
{
    @Autowired
    private DepartmentRewardMapper departmentRewardMapper;

    @Autowired
    private DepartmentMapper departmentMapper;

    @Resource
    private DepartmentTotalScoreMapper departmentTotalScoreMapper;

    @Resource
    private DeptLockMapper deptLockMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private HiveFeignClient hiveFeignClient;

    @Autowired
    private BusinessStatisticsImpl businessStatistics;

    @Value ("${my.orgCode}")
    private String orgCode;

    @Resource
    private KaoPinMapper kaoPinMapper;

    @Autowired
    private SystemConfigService systemConfigService;


    @Transactional (rollbackFor = Exception.class)
    public List<DepartmentReward> getDepartmentReward(String month)
    {
        Weekend<DepartmentReward> weekend = new Weekend<>(DepartmentReward.class);
        WeekendCriteria<DepartmentReward, Object> criteria = weekend.weekendCriteria();
        criteria.andEqualTo(DepartmentReward::getMonth, month);
        List<DepartmentReward> list = departmentRewardMapper.selectByExample(weekend);
        if (CollectionUtils.isEmpty(list))
        {
            List<DepartmentReward> departmentRewardList = new ArrayList<>();

            List<Department> departmentList = departmentMapper.selectAll();
            for (Department department : departmentList)
            {
                DepartmentReward reward = new DepartmentReward();
                reward.setMonth(month);
                reward.setDeptId(department.getId());
                reward.setDeptName(department.getOrganizationName());
                reward.setCoefficient(new BigDecimal(1));
                departmentRewardList.add(reward);
            }
            insertOrUpdateReward(departmentRewardList);
            return departmentRewardMapper.selectByExample(weekend);
        }
        return list;
    }


    @Transactional (rollbackFor = Exception.class)
    public void insertOrUpdateReward(List<DepartmentReward> departmentRewardList)
    {
        if (CollectionUtils.isEmpty(departmentRewardList))
        {
            return;
        }
        departmentRewardList.forEach(departmentReward -> {
            Weekend<DepartmentReward> weekend = new Weekend<>(DepartmentReward.class);
            WeekendCriteria<DepartmentReward, Object> criteria = weekend.weekendCriteria();
            criteria.andEqualTo(DepartmentReward::getMonth, departmentReward.getMonth());
            criteria.andEqualTo(DepartmentReward::getDeptId, departmentReward.getDeptId());
            DepartmentReward reward = departmentRewardMapper.selectOne(new DepartmentReward().setDeptId(departmentReward.getDeptId())
                                                                                             .setMonth(departmentReward.getMonth()));
            if (Objects.nonNull(departmentReward.getDeptReward()))
            {
                departmentReward.setDeptReward(departmentReward.getDeptReward().setScale(2, BigDecimal.ROUND_DOWN));
            }
            if (Objects.isNull(reward))
            {
                departmentRewardMapper.insertSelective(departmentReward);
            }
            else
            {
                departmentRewardMapper.updateByPrimaryKeySelective(departmentReward);
            }
        });
    }


    @Transactional
    public List<Department> insertOrUpdate()
    {
        List<Department> departmentList = hiveFeignClient.getSubOrganization(orgCode);
        for (Department dept : departmentList)
        {
            Department department = departmentMapper.selectOne(new Department().setId(dept.getId()).setDeptId(dept.getId()));
            if (Objects.nonNull(department))
            {
                departmentMapper.updateByPrimaryKeySelective(dept);
            }
            else
            {
                dept.setDeptId(dept.getId());
                departmentMapper.insertSelective(dept);
            }
        }
        return departmentList;
    }


    public List<Department> getDeptList()
    {
        return departmentMapper.selectAll();
    }


    public List<DepartmentScoreSummary> scoreSummary(String month)
    {
        Map<Integer, Long> deptEmployeeNumberMap = this.deptEmployeeNumberExcludeZero(month);
        List<Department> departmentList = new ArrayList<>();
        if (MchInterepter.UID.get().getRole() == 3)
        {
            return Collections.emptyList();
        }
        else if (MchInterepter.UID.get().getRole() == 2)
        {
            Weekend<User> weekend2 = new Weekend<>(User.class);
            WeekendCriteria<User, Object> criteria2 = weekend2.weekendCriteria();
            criteria2.andEqualTo(User::getUserCode, MchInterepter.UID.get().getUid());
            User user = userMapper.selectOneByExample(weekend2);
            Department department = departmentMapper.selectByPrimaryKey(user.getDeptId());
            if (Objects.isNull(department))
            {
                throw new BusinessException("当前部门不存在:" + user.getDeptId());
            }
            departmentList.add(department);
        }
        else
        {
            departmentList.addAll(departmentMapper.selectAll());

        }
        Map<String, SystemConfig> scoreWeight = systemConfigService.queryConfigByMenu("scoreWeight");
        BigDecimal determinationScoreWeight = scoreWeight.get("determinationScore")
                                                         .getValue()
                                                         .divide(new BigDecimal("100"), 2, RoundingMode.DOWN);
        BigDecimal allowanceScoreWeight = scoreWeight.get("allowanceScore").getValue().divide(new BigDecimal("100"), 2, RoundingMode.DOWN);

        return departmentList.stream().map(department -> {
            Weekend<DepartmentTotalScore> weekend = new Weekend<>(DepartmentTotalScore.class);
            WeekendCriteria<DepartmentTotalScore, Object> criteria = weekend.weekendCriteria();
            criteria.andEqualTo(DepartmentTotalScore::getMonth, month);
            criteria.andEqualTo(DepartmentTotalScore::getDeptId, department.getDeptId());
            DepartmentTotalScore departmentTotalScore = departmentTotalScoreMapper.selectOneByExample(weekend);
            DepartmentScoreSummary summary = new DepartmentScoreSummary();
            if (Objects.nonNull(departmentTotalScore))
            {
                BeanUtils.copyProperties(departmentTotalScore, summary);
                BigDecimal totalScore = departmentTotalScore.getDeterminationScore()
                                                            .multiply(determinationScoreWeight)
                                                            .add(departmentTotalScore.getAllowanceScore().multiply(allowanceScoreWeight))
                                                            .add(departmentTotalScore.getDeltaScore());
                summary.setTotalScore(totalScore);
                summary.setAccumulativePoints(this.queryAccumulativePoints(month, departmentTotalScore.getDeptId()));

                Long employeeCount = deptEmployeeNumberMap.get(department.getDeptId());
                if (Objects.nonNull(employeeCount) && employeeCount > 0)
                {
                    BigDecimal avg = summary.getTotalScore().divide(new BigDecimal(employeeCount), 2, RoundingMode.DOWN);
                    summary.setAvgScore(avg);
                }
            }
            summary.setDeptId(department.getDeptId());
            summary.setDeptName(department.getOrganizationName());
            return summary;
        }).collect(Collectors.toList());
    }


    private BigDecimal queryAccumulativePoints(String month, Integer deptId)
    {
        Weekend<DepartmentTotalScore> weekend = new Weekend<>(DepartmentTotalScore.class);
        WeekendCriteria<DepartmentTotalScore, Object> criteria = weekend.weekendCriteria();
        criteria.andCondition("substr(month, 1,4) = " + month.substring(0, 4));
        criteria.andEqualTo(DepartmentTotalScore::getDeptId, deptId);
        List<DepartmentTotalScore> totalScores = departmentTotalScoreMapper.selectByExample(weekend);
        if (CollectionUtils.isEmpty(totalScores))
        {
            return BigDecimal.ZERO;
        }
        return totalScores.stream().map(DepartmentTotalScore::getPoints).reduce(BigDecimal.ZERO, BigDecimal::add);
    }


    public List<LinkedHashMap<String, String>> usersScoreSummary(String month, Integer deptId)
    {
        String userCode = null;
        if (MchInterepter.UID.get().getRole() == 3)
        {
            userCode = MchInterepter.UID.get().getUid();
            deptId = null;
        }
        else if (MchInterepter.UID.get().getRole() == 2)
        {
            Weekend<User> weekend = new Weekend<>(User.class);
            WeekendCriteria<User, Object> criteria = weekend.weekendCriteria();
            criteria.andEqualTo(User::getUserCode, MchInterepter.UID.get().getUid());
            User user = userMapper.selectOneByExample(weekend);
            deptId = user.getDeptId();
        }
        return businessStatistics.queryBusinessStatistics(month, deptId, userCode);
    }


    public List<KaopinDto> getKaopin(String month)
    {
        Map<Integer, Long> employeeNumberMap = this.deptEmployeeNumberExcludeZero(month);
        Map<String, SystemConfig> scoreWeight = systemConfigService.queryConfigByMenu("scoreWeight");
        Weekend<KaoPin> weekend = new Weekend<>(KaoPin.class);
        WeekendCriteria<KaoPin, Object> criteria = weekend.weekendCriteria();
        criteria.andEqualTo(KaoPin::getMonth, month);
        List<KaoPin> kaoPins = kaoPinMapper.selectByExample(weekend);
        if (CollectionUtils.isEmpty(kaoPins))
        {
            kaoPins = this.initKaoPin(month);
        }
        return kaoPins.stream().map(kaoPin -> {
            KaopinDto kaopinDto = new KaopinDto();
            BeanUtils.copyProperties(kaoPin, kaopinDto);
            //重新查询自考分，实时计算最新的
            kaopinDto.setZiKao(this.getZiKao(month, kaoPin.getDeptId(), employeeNumberMap, scoreWeight));
            kaoPin.setZiKao(kaopinDto.getZiKao());
            kaopinDto.setTotal(this.totalScore(kaoPin));
            return kaopinDto;
        }).collect(Collectors.toList());
    }


    public void updateKaoPin(String month, List<KaopinRequest> kaopinRequests)
    {
        Map<Integer, Long> employeeNumberMap = this.deptEmployeeNumberExcludeZero(month);
        Map<String, SystemConfig> scoreWeight = systemConfigService.queryConfigByMenu("scoreWeight");
        kaopinRequests.forEach(kaoPinRequest -> {
            if (Objects.isNull(kaoPinRequest.getDeptId()))
            {
                throw new BusinessException("部门id不能为空");
            }
            KaoPin query = new KaoPin();
            query.setDeptId(kaoPinRequest.getDeptId());
            query.setMonth(month);
            KaoPin dbKaopin = kaoPinMapper.selectOne(query);
            if (Objects.isNull(dbKaopin))
            {
                return;
            }
            KaoPin kaoPin = new KaoPin();
            BeanUtils.copyProperties(kaoPinRequest, kaoPin);
            kaoPin.setZiKao(this.getZiKao(month, kaoPinRequest.getDeptId(), employeeNumberMap, scoreWeight));
            kaoPin.setId(dbKaopin.getId());
            kaoPin.setTotal(this.totalScore(kaoPin));
            kaoPinMapper.updateByPrimaryKeySelective(kaoPin);
        });
    }


    private List<KaoPin> initKaoPin(String month)
    {
        List<Department> departmentList = departmentMapper.selectAll();
        if (CollectionUtils.isEmpty(departmentList))
        {
            return new ArrayList<>();
        }

        Map<Integer, Long> employeeNumberMap = this.deptEmployeeNumberExcludeZero(month);
        Map<String, SystemConfig> scoreWeight = systemConfigService.queryConfigByMenu("scoreWeight");

        return departmentList.stream().map(department -> {
            KaoPin newKaoPin = getNewKaoPin(month, department, employeeNumberMap, scoreWeight);
            kaoPinMapper.insertSelective(newKaoPin);
            return newKaoPin;
        }).collect(Collectors.toList());
    }


    private KaoPin getNewKaoPin(String month,
                                Department department,
                                Map<Integer, Long> employeeNumberMap,
                                Map<String, SystemConfig> scoreWeight)
    {
        KaoPin kaoPin = new KaoPin();
        BeanUtils.copyProperties(new KaopinRequest().getDefault(), kaoPin);
        kaoPin.setZiKao(this.getZiKao(month, department.getDeptId(), employeeNumberMap, scoreWeight));
        kaoPin.setDeptId(department.getId());
        kaoPin.setDeptName(department.getOrganizationName());
        kaoPin.setMonth(month);
        kaoPin.setTotal(this.totalScore(kaoPin));
        return kaoPin;
    }


    private BigDecimal getZiKao(String month, Integer deptId, Map<Integer, Long> employeeNumberMap, Map<String, SystemConfig> scoreWeight)
    {
        Weekend<DepartmentTotalScore> weekend = new Weekend<>(DepartmentTotalScore.class);
        WeekendCriteria<DepartmentTotalScore, Object> weekendCriteria = weekend.weekendCriteria();
        weekendCriteria.andEqualTo(DepartmentTotalScore::getDeptId, deptId);
        weekendCriteria.andEqualTo(DepartmentTotalScore::getMonth, month);
        DepartmentTotalScore deptTotalScore = departmentTotalScoreMapper.selectOneByExample(weekend);
        //还要算部门人数
        long count = employeeNumberMap.getOrDefault(deptId, 0L);
        if (count == 0)
        {
            return BigDecimal.ZERO;
        }

        BigDecimal determinationScoreWeight = scoreWeight.get("determinationScore")
                                                         .getValue()
                                                         .divide(new BigDecimal("100"), 2, RoundingMode.DOWN);
        BigDecimal allowanceScoreWeight = scoreWeight.get("allowanceScore").getValue().divide(new BigDecimal("100"), 2, RoundingMode.DOWN);
        BigDecimal totalScore = Objects.isNull(deptTotalScore) ?
                BigDecimal.ZERO :
                deptTotalScore.getDeterminationScore()
                              .multiply(determinationScoreWeight)
                              .add(deptTotalScore.getAllowanceScore().multiply(allowanceScoreWeight))
                              .add(deptTotalScore.getDeltaScore());
        return totalScore.divide(new BigDecimal(count), 2, RoundingMode.DOWN);
    }


    private BigDecimal totalScore(KaoPin kaoPin)
    {
        BigDecimal leader = kaoPin.getZongBian()
                                  .multiply(new BigDecimal("0.05"))
                                  .add(kaoPin.getOffice().multiply(new BigDecimal("0.05")))
                                  .add(kaoPin.getCaiWu().multiply(new BigDecimal("0.05")))
                                  .add(kaoPin.getFenGuan().multiply(new BigDecimal("0.1")))
                                  .add(kaoPin.getLunZhi().multiply(new BigDecimal("0.15")))
                                  .add(kaoPin.getDangWei().multiply(new BigDecimal("0.3")))
                                  .add(kaoPin.getZhuYao().multiply(new BigDecimal("0.3")));
        return kaoPin.getZiKao().multiply(new BigDecimal("0.2")).add(leader.multiply(new BigDecimal("0.8")));
    }


    /**
     * 获取当前总分不为0的用户
     *
     * @return
     */
    public Map<Integer, Long> deptEmployeeNumberExcludeZero(String month)
    {
        Map<String, SystemConfig> scoreWeight = systemConfigService.queryConfigByMenu("scoreWeight");
        BigDecimal determinationScoreWeight = scoreWeight.get("determinationScore").getValue().divide(new BigDecimal("100"), 2,
                RoundingMode.DOWN);
        BigDecimal allowanceScoreWeight = scoreWeight.get("allowanceScore").getValue().divide(new BigDecimal("100"), 2, RoundingMode.DOWN);
        return userMapper.deptEmployeeNumberExcludeZero(month, determinationScoreWeight, allowanceScoreWeight)
                         .stream()
                         .collect(Collectors.toMap(UserReduceDto::getDeptId, UserReduceDto::getCount));

    }

    public int updateDeptLock(Integer deptId, String month, Integer status)
    {
        DeptLock deptLock = new DeptLock();
        deptLock.setDeptId(deptId);
        deptLock.setMonth(month);
        deptLock.setStatus(status);
        return deptLockMapper.insertDeptLock(deptLock);
    }

    public DeptLock getDeptLock(Integer deptId, String month)
    {
        DeptLock deptLock = new DeptLock();
        deptLock.setDeptId(deptId);
        deptLock.setMonth(month);
        return deptLockMapper.selectOne(deptLock);
    }
}
